package loadbalance;

import lombok.Data;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 加权轮询算法负载均衡
 * @author Daniel
 */
public class RoundRobinLoadBalance {

    /**
     * AtomicPositiveInteger 用于记录服务的调用编号
     */
    private final AtomicPositiveInteger sequence = new AtomicPositiveInteger();

    public Invoker doSelect(List<Invoker> invokers) {
        int length = invokers.size();
        // 最大权重
        int maxWeight = 0;
        // 最小权重
        int minWeight = Integer.MAX_VALUE;
        final LinkedHashMap<Invoker, IntegerWrapper> invokerToWeightMap = new LinkedHashMap<>();
        // 权重总和
        int weightSum = 0;

        // 下面这个循环主要用于查找最大和最小权重，计算权重总和等
        for (int i = 0; i < length; i++) {
            Invoker invoker = invokers.get(i);
            int weight = WeightUtil.getWeight(invoker.getWeight(), invoker.getUptime());
            // 获取最大和最小权重
            maxWeight = Math.max(maxWeight, weight);
            minWeight = Math.min(minWeight, weight);
            if (weight > 0) {
                // 将 weight 封装到 IntegerWrapper 中
                invokerToWeightMap.put(invokers.get(i), new IntegerWrapper(weight));
                // 累加权重
                weightSum += weight;
            }
        }

        // 获取当前的调用编号
        int currentSequence = sequence.getAndIncrement();
        // 如果最小权重小于最大权重，表明服务提供者之间的权重是不相等的
        if (maxWeight > 0 && minWeight < maxWeight) {
            // 使用调用编号对权重总和进行取余操作
            int mod = currentSequence % weightSum;
            // 进行 maxWeight 次遍历
            for (int i = 0; i < maxWeight; i++) {
                // 遍历 invokerToWeightMap
                for (Map.Entry<Invoker, IntegerWrapper> each : invokerToWeightMap.entrySet()) {
                    // 获取 Invoker
                    final Invoker k = each.getKey();
                    // 获取权重包装类 IntegerWrapper
                    final IntegerWrapper v = each.getValue();

                    // 如果 mod = 0，且权重大于0，此时返回相应的 Invoker
                    if (mod == 0 && v.getValue() > 0) {
                        return k;
                    }

                    // mod != 0，且权重大于0，此时对权重和 mod 分别进行自减操作
                    if (v.getValue() > 0) {
                        v.decrement();
                        mod--;
                    }
                }
            }
        }

        // 服务提供者之间的权重相等，此时通过轮询选择 Invoker
        return invokers.get(currentSequence % length);
    }
    // IntegerWrapper 是一个 int 包装类，主要包含了一个自减方法。
    @Data
    private static final class IntegerWrapper {
        private int value;

        public IntegerWrapper(int value) {
            this.value = value;
        }

        public void decrement() {
            this.value--;
        }

        // 省略部分代码
    }

    public static class AtomicPositiveInteger extends Number {

        private static final long serialVersionUID = -3038533876489105940L;

        private final AtomicInteger i;

        public AtomicPositiveInteger() {
            i = new AtomicInteger();
        }

        public AtomicPositiveInteger(int initialValue) {
            i = new AtomicInteger(initialValue);
        }

        public final int getAndIncrement() {
            for (; ; ) {
                int current = i.get();
                int next = (current >= Integer.MAX_VALUE ? 0 : current + 1);
                if (i.compareAndSet(current, next)) {
                    return current;
                }
            }
        }

        public final int getAndDecrement() {
            for (; ; ) {
                int current = i.get();
                int next = (current <= 0 ? Integer.MAX_VALUE : current - 1);
                if (i.compareAndSet(current, next)) {
                    return current;
                }
            }
        }

        public final int incrementAndGet() {
            for (; ; ) {
                int current = i.get();
                int next = (current >= Integer.MAX_VALUE ? 0 : current + 1);
                if (i.compareAndSet(current, next)) {
                    return next;
                }
            }
        }

        public final int decrementAndGet() {
            for (; ; ) {
                int current = i.get();
                int next = (current <= 0 ? Integer.MAX_VALUE : current - 1);
                if (i.compareAndSet(current, next)) {
                    return next;
                }
            }
        }

        public final int get() {
            return i.get();
        }

        public final void set(int newValue) {
            if (newValue < 0) {
                throw new IllegalArgumentException("new value " + newValue + " < 0");
            }
            i.set(newValue);
        }

        public final int getAndSet(int newValue) {
            if (newValue < 0) {
                throw new IllegalArgumentException("new value " + newValue + " < 0");
            }
            return i.getAndSet(newValue);
        }

        public final int getAndAdd(int delta) {
            if (delta < 0) {
                throw new IllegalArgumentException("delta " + delta + " < 0");
            }
            for (; ; ) {
                int current = i.get();
                int next = (current >= Integer.MAX_VALUE - delta + 1 ? delta - 1 : current + delta);
                if (i.compareAndSet(current, next)) {
                    return current;
                }
            }
        }

        public final int addAndGet(int delta) {
            if (delta < 0) {
                throw new IllegalArgumentException("delta " + delta + " < 0");
            }
            for (; ; ) {
                int current = i.get();
                int next = (current >= Integer.MAX_VALUE - delta + 1 ? delta - 1 : current + delta);
                if (i.compareAndSet(current, next)) {
                    return next;
                }
            }
        }

        public final boolean compareAndSet(int expect, int update) {
            if (update < 0) {
                throw new IllegalArgumentException("update value " + update + " < 0");
            }
            return i.compareAndSet(expect, update);
        }

        public final boolean weakCompareAndSet(int expect, int update) {
            if (update < 0) {
                throw new IllegalArgumentException("update value " + update + " < 0");
            }
            return i.weakCompareAndSet(expect, update);
        }

        @Override
        public byte byteValue() {
            return i.byteValue();
        }

        @Override
        public short shortValue() {
            return i.shortValue();
        }

        @Override
        public int intValue() {
            return i.intValue();
        }

        @Override
        public long longValue() {
            return i.longValue();
        }

        @Override
        public float floatValue() {
            return i.floatValue();
        }

        @Override
        public double doubleValue() {
            return i.doubleValue();
        }

        @Override
        public String toString() {
            return i.toString();
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + i.hashCode();
            return result;
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj) {
                return true;
            }
            if (!(obj instanceof AtomicPositiveInteger)) {
                return false;
            }
            AtomicPositiveInteger other = (AtomicPositiveInteger) obj;
            return i.intValue() == other.i.intValue();
        }

    }
}
